#include "stdafx.h"
#include "CppUnitTest.h"

#include <sstream>

#include "../ETTriangleIntersectionLib/MollerTriTriIntersect.cpp"

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

namespace Microsoft
	{
	namespace VisualStudio
		{
		namespace CppUnitTestFramework
			{
			template<> static std::wstring ToString<Point3D>(const Point3D& t)
				{
				std::ostringstream str_stream;
				str_stream << "(" << t.m_x << ", " << t.m_y << ", " << t.m_z << ")";
				auto s = str_stream.str();
				RETURN_WIDE_STRING(std::wstring(s.begin(), s.end()));
				};

			template<> static std::wstring ToString<std::pair<double, double>>(const std::pair<double, double>& t)
				{
				std::ostringstream str_stream;
				str_stream << "(" << t.first << ", " << t.second << ")";
				auto s = str_stream.str();
				RETURN_WIDE_STRING(std::wstring(s.begin(), s.end()));
				};
			}
		}
	}

namespace ETTriangleIntersectTests
{		
	TEST_CLASS(UnitTest1)
	{
	public:

		TEST_METHOD(Point3DEqualityTest)
			{
			// Arrange
			auto point_1 = Point3D{ 1, 2, 3 };
			auto point_2 = Point3D{ -2, 4, 1 };
			auto point_3 = Point3D{ 1, 2, 3 };

			// Act, Assert
			Assert::AreEqual(point_1, point_3);
			Assert::AreNotEqual(point_1, point_2);
			Assert::AreNotEqual(point_2, point_3);
			}
		
		TEST_METHOD(CrossProductTest)
		{
		// Arrange
		auto vector_1_1 = Point3D{ 1, 2, 3 };
		auto vector_1_2 = Point3D{ -2, 4, -1 };
		auto expected_1 = Point3D{ -14, -5, 8 };

		auto vector_2_1 = Point3D{ -2, 2.5, .25 };
		auto vector_2_2 = Point3D{ -1.5, 8, -1.25 };
		auto expected_2 = Point3D{ -5.125, -2.875, -12.25 };

		// Act
		auto actual_1 = CrossProduct(vector_1_1, vector_1_2);
		auto actual_2 = CrossProduct(vector_2_1, vector_2_2);

		// Assert
		Assert::AreEqual(expected_1, actual_1);
		Assert::AreEqual(expected_2, actual_2);
		}

		TEST_METHOD(DotProductTest)
			{

			// Arrange
			auto vector_1_1 = Point3D{ 17, -13, .5 };
			auto vector_1_2 = Point3D{ -4.5, 19, 11.7 };
			double expected_1 = -317.65;

			auto vector_2_1 = Point3D{ 5, 12, 13 };
			auto vector_2_2 = Point3D{ 1, -1, 2 };
			double expected_2 = 19.0;

			// Act
			auto actual_1 = DotProduct(vector_1_1, vector_1_2);
			auto actual_2 = DotProduct(vector_2_1, vector_2_2);

			// Assert
			Assert::AreEqual(expected_1, actual_1);
			Assert::AreEqual(expected_2, actual_2);
			}

		TEST_METHOD(VectorSubstitutionTest)
			{
			// Arrange
			auto vector_1_1 = Point3D{ 17, -13, .5 };
			auto vector_1_2 = Point3D{ -4.5, 19, 11.7 };
			auto expected_1 = Point3D{ 21.5, -32, -11.2 };

			auto vector_2_1 = Point3D{ 5, 12, 13 };
			auto vector_2_2 = Point3D{ 1, -1, 2 };
			auto expected_2 = Point3D{4, 13, 11};

			// Act
			auto actual_1 = Substitute(vector_1_1, vector_1_2);
			auto actual_2 = Substitute(vector_2_1, vector_2_2);

			// Assert
			Assert::AreEqual(expected_1, actual_1);
			Assert::AreEqual(expected_2, actual_2);
			}

		TEST_METHOD(ShouldReturnTrueWhenPointInsideTriangle)
			{
			// Arrange
			auto test_triangle = Triangle3D{ Point3D{ 1, 0, 0 }, Point3D{ 0, 1, 0 }, Point3D{ 0, 0, 1 } };
			auto test_point = Point3D{ 0.5, 0.25, 0.25 };
			auto test_vertex = Point3D{ 0, 1, 0 };

			// Act
			bool actual = PointInsideTriangle(test_triangle, test_point);
			bool actual_vertex = PointInsideTriangle(test_triangle, test_vertex);
			// Assert

			Assert::IsTrue(actual);
			Assert::IsTrue(actual_vertex);
			}

		TEST_METHOD(AbsTest)
			{

			// Arrange
			auto vector_1 = Point3D{ 5, -12, 0 };
			auto vector_2 = Point3D{ -2, 3, -6 };
			double expected_1 = 13.0;
			double expected_2 = 7.0;

			// Act
			auto actual_1 = Abs(vector_1);
			auto actual_2 = Abs(vector_2);

			// Assert
			Assert::AreEqual(expected_1, actual_1);
			Assert::AreEqual(expected_2, actual_2);
			}

		TEST_METHOD(ShouldSwapPairWhenFirstIsBiggerThanSecond)
			{
			// Arrange
			auto p1 = std::pair<double, double>(1.0001, 1.0);
			auto p2 = std::pair<double, double>(2.001, 2.002);

			//Act
			SortPair(p1);
			SortPair(p2);

			//Assert
			Assert::AreEqual(std::pair<double, double>(1.0, 1.0001), p1);
			Assert::AreEqual(std::pair<double, double>(2.001, 2.002), p2);
			}

		TEST_METHOD(LessThanEpsilonShouldReturn0WhenAbsIsLessThanEPSILONElseAbs)
			{
			// Arrange
			double i = 1e-9;
			double j = -1;
			double k = -1e-10;

			// Act
			auto x_i = LessThanEpsilon(i);
			auto x_j = LessThanEpsilon(j);
			auto x_k = LessThanEpsilon(k);

			// Assert
			Assert::AreEqual(x_i, 0.0);
			Assert::AreEqual(x_j, 1.0);
			Assert::AreEqual(x_k, 0.0);
			}

		TEST_METHOD(ShouldReturnTrueWhenTrianglesIntersect)
			{
			// Arrange
			auto tr1 = Triangle3D{ Point3D{ 2, 0, 0 }, Point3D{ 0, -1, 0 }, Point3D{ 0, 1, 0 } };
			auto tr2 = Triangle3D{ Point3D{ 3, 0, 1 }, Point3D{ 1, 0, 1 }, Point3D{ -1, 0, -1 } };

			// Act
			bool result = AreTrianglesIntersecting(tr1, tr2);

			// Assert
			Assert::IsTrue(result);
			}
		TEST_METHOD(SholudReturnFalseWhenTrianglesDoNotIntersect)
			{
			// Arrange
			auto tr1 = Triangle3D{ Point3D{ 2, 0, 0 }, Point3D{ 0, -1, 0 }, Point3D{ 0, 1, 0 } };
			auto tr2= Triangle3D{ Point3D{ 9, 0, 1 }, Point3D{ 11, 0, 1 }, Point3D{ 10, 0, -1 } };

			// Act
			bool result = AreTrianglesIntersecting(tr1, tr2);

			// Assert
			Assert::IsFalse(result);
			}

		TEST_METHOD(ShouldReturnTrueWhenShareEdgeOrVertex)
			{
			// Arrange
			auto tr1_vertex = Triangle3D{ Point3D{ 2, 0, 0 }, Point3D{ 0, -1, 0 }, Point3D{ 0, 1, 0 } };
			auto tr2_vertex = Triangle3D{ Point3D{ 2, 0, 0 }, Point3D{ 1, 1, 1 }, Point3D{ 1, 2, 2 } };

			auto tr1_edge = Triangle3D{ Point3D{ 2, 0, 0 }, Point3D{ 0, -1, 0 }, Point3D{ 0, 1, 0 } };
			auto tr2_edge = Triangle3D{ Point3D{ 2, 0, 0 }, Point3D{ 0, -1, 0 }, Point3D{ 1, 2, 2 } };

			// Act
			bool result_vertex = AreTrianglesIntersecting(tr1_vertex, tr2_vertex);
			bool result_edge = AreTrianglesIntersecting(tr1_edge, tr2_edge);

			// Assert
			Assert::IsTrue(result_vertex);
			Assert::IsTrue(result_edge);
			}

	};
}